#include "process_main.h"
#include "utils.h"

static char msg_buff[3000];
static int msg_top=0;

static int  write_buf(char *start, int maxlength){

	char *end = start;
	while(*end)
		end++;
	int length= end - start;
	if(length>maxlength)return -1;
	int res =  memcpy(msg_top+msg_buff,start,length);
	msg_top += length;
	return res;
}
	


procps_status_t * procps_scan(int save_user_arg0)
{
        static DIR *dir;
        struct dirent *entry;
        static procps_status_t ret_status;
        char *name;
        int n;
        char status[32];
        char buf[1024];
        FILE *fp;
        procps_status_t curstatus;
        int pid;
        long tasknice;
        struct stat sb;

        if (!dir) {
                dir = opendir("/proc");
                if(!dir)
                        print_error_msg_and_die("Can't open /proc");
        }
        for(;;) {
                if((entry = readdir(dir)) == NULL) {
                        closedir(dir);
                        dir = 0;
                        return 0;
                }
                name = entry->d_name;
                if (!(*name >= '0' && *name <= '9'))
                        continue;

                memset(&curstatus, 0, sizeof(procps_status_t));
                pid = atoi(name);
                curstatus.pid = pid;

                sprintf(status, "/proc/%d", pid);
                if(stat(status, &sb))
                        continue;
               // memcpy(curstatus.user, sb.st_uid, sizeof(curstatus.user));

                sprintf(status, "/proc/%d/stat", pid);

                if((fp = fopen(status, "r")) == NULL)
                        continue;
                name = fgets(buf, sizeof(buf), fp);
                fclose(fp);
                if(name == NULL)
                        continue;
                name = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
                if(name == 0 || name[1] != ' ')
                        continue;
                *name = 0;
                sscanf(buf, "%*s (%15c", curstatus.short_cmd);
                n = sscanf(name+2,
                "%c %d "
                "%*s %*s %*s %*s "     /* pgrp, session, tty, tpgid */
                "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
                "%lu %lu "
                "%*s %*s %*s "         /* cutime, cstime, priority */
                "%ld "
                "%*s %*s %*s "         /* timeout, it_real_value, start_time */
                "%*s "                 /* vsize */
                "%ld",
                curstatus.state, &curstatus.ppid,
                &curstatus.utime, &curstatus.stime,
                &tasknice,
                &curstatus.rss);
                if(n != 6)
                        continue;

                if (curstatus.rss == 0 && curstatus.state[0] != 'Z')
                        curstatus.state[1] = 'W';
                else
                        curstatus.state[1] = ' ';
                if (tasknice < 0)
                        curstatus.state[2] = '<';
                else if (tasknice > 0)
                        curstatus.state[2] = 'N';
                else
                        curstatus.state[2] = ' ';

#ifdef PAGE_SHIFT
                curstatus.rss <<= (PAGE_SHIFT - 10);     /* 2**10 = 1kb */
#else
                curstatus.rss *= (getpagesize() >> 10);     /* 2**10 = 1kb */
#endif

                if(save_user_arg0) {
                        sprintf(status, "/proc/%d/cmdline", pid);
                        if((fp = fopen(status, "r")) == NULL)
                                continue;
                        if((n=fread(buf, 1, sizeof(buf)-1, fp)) > 0) {
                                if(buf[n-1]=='\n')
                                        buf[--n] = 0;
                                name = buf;
                                while(n) {
                                        if(((unsigned char)*name) < ' ')
                                                *name = ' ';
                                        name++;
                                        n--;
                                }
                                *name = 0;
                                if(buf[0])
                                        curstatus.cmd = strdup(buf);
                                /* if NULL it work true also */
                        }
                        fclose(fp);
                }
                return memcpy(&ret_status, &curstatus, sizeof(procps_status_t));
        }
}


typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q);

static procps_status_t *top;   /* Hehe */
static int ntop;


static int mem_sort (procps_status_t *P, procps_status_t *Q)
{
    return (int)(Q->rss - P->rss);
}


#define sort_depth 3
static cmp_t sort_function[sort_depth];

static int pcpu_sort (procps_status_t *P, procps_status_t *Q)
{
    return (Q->pcpu - P->pcpu);
}

static int time_sort (procps_status_t *P, procps_status_t *Q)
{
    return (int)((Q->stime + Q->utime) - (P->stime + P->utime));
}

static int mult_lvl_cmp(void* a, void* b) {
    int i, cmp_val;

    for(i = 0; i < sort_depth; i++) {
        cmp_val = (*sort_function[i])(a, b);
        if (cmp_val != 0)
            return cmp_val;
    }
    return 0;
}

/* This structure stores some critical information from one frame to
   the next. mostly used for sorting. Added cumulative and resident fields. */
struct save_hist {
    int ticks;
    int pid;
    int utime;
    int stime;
};

/*
 * Calculates percent cpu usage for each task.
 */

static struct save_hist *save_history;

static unsigned long Hertz;

static void init_Hertz_value(void) {
  unsigned long user_j, nice_j, sys_j, other_j;  /* jiffies (clock ticks) */
  double up_1, up_2, seconds;
  unsigned long jiffies, h;
  char buf[80];
  int uptime_fd = -1;
  int stat_fd = -1;

  long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF);

  if(smp_num_cpus<1) smp_num_cpus=1;
  do {
    int local_n;

    FILE_TO_BUF("uptime", uptime_fd);
    up_1 = strtod(buf, 0);
    FILE_TO_BUF("stat", stat_fd);
    sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j);
    FILE_TO_BUF2("uptime", uptime_fd);
    up_2 = strtod(buf, 0);
  } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */

  close(uptime_fd);
  close(stat_fd);

  jiffies = user_j + nice_j + sys_j + other_j;
  seconds = (up_1 + up_2) / 2;
  h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus );
  /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
  switch(h){
  case   30 ...   34 :  Hertz =   32; break; /* ia64 emulator */
  case   48 ...   52 :  Hertz =   50; break;
  case   58 ...   62 :  Hertz =   60; break;
  case   63 ...   65 :  Hertz =   64; break; /* StrongARM /Shark */
  case   95 ...  105 :  Hertz =  100; break; /* normal Linux */
  case  124 ...  132 :  Hertz =  128; break; /* MIPS, ARM */
  case  195 ...  204 :  Hertz =  200; break; /* normal << 1 */
  case  253 ...  260 :  Hertz =  256; break;
  case  295 ...  304 :  Hertz =  300; break; /* 3 cpus */
  case  393 ...  408 :  Hertz =  400; break; /* normal << 2 */
  case  495 ...  504 :  Hertz =  500; break; /* 5 cpus */
  case  595 ...  604 :  Hertz =  600; break; /* 6 cpus */
  case  695 ...  704 :  Hertz =  700; break; /* 7 cpus */
  case  790 ...  808 :  Hertz =  800; break; /* normal << 3 */
  case  895 ...  904 :  Hertz =  900; break; /* 9 cpus */
  case  990 ... 1010 :  Hertz = 1000; break; /* ARM */
  case 1015 ... 1035 :  Hertz = 1024; break; /* Alpha, ia64 */
  case 1095 ... 1104 :  Hertz = 1100; break; /* 11 cpus */
  case 1180 ... 1220 :  Hertz = 1200; break; /* Alpha */
  default:
    /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
    Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
  }
}

static void do_stats(void)
{
    struct timeval t;
    static struct timeval oldtime;
    struct timezone timez;
    float elapsed_time;

    procps_status_t *cur;
    int total_time, i, n;
    static int prev_count;
    int systime, usrtime, pid;

    struct save_hist *New_save_hist;

    /*
     * Finds the current time (in microseconds) and calculates the time
     * elapsed since the last update.
     */
    gettimeofday(&t, &timez);
    elapsed_time = (t.tv_sec - oldtime.tv_sec)
        + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
    oldtime.tv_sec  = t.tv_sec;
    oldtime.tv_usec = t.tv_usec;

    New_save_hist  = alloca(sizeof(struct save_hist)*ntop);
    /*
     * Make a pass through the data to get stats.
     */
    for(n = 0; n < ntop; n++) {
        cur = top + n;

        /*
         * Calculate time in cur process.  Time is sum of user time
         * (usrtime) plus system time (systime).
         */
        systime = cur->stime;
        usrtime = cur->utime;
        pid = cur->pid;
        total_time = systime + usrtime;
        New_save_hist[n].ticks = total_time;
        New_save_hist[n].pid = pid;
        New_save_hist[n].stime = systime;
        New_save_hist[n].utime = usrtime;

        /* find matching entry from previous pass */
        for (i = 0; i < prev_count; i++) {
            if (save_history[i].pid == pid) {
                total_time -= save_history[i].ticks;
                systime -= save_history[i].stime;
                usrtime -= save_history[i].utime;
                break;
            }
        }

        /*
         * Calculate percent cpu time for cur task.
         */
        i = (total_time * 10 * 100/Hertz) / elapsed_time;
        if (i > 999)
            i = 999;
        cur->pcpu = i;

    }

    /*
     * Save cur frame's information.
     */
    free(save_history);
    save_history = memcpy(safe_malloc(sizeof(struct save_hist)*n), New_save_hist, sizeof(struct save_hist)*n);
    prev_count = n;
    qsort(top, n, sizeof(procps_status_t), (void*)mult_lvl_cmp);
}


/* display generic info (meminfo / loadavg) */
static unsigned long display_generic()
{
        FILE *fp;
        char buf[80];
	char text[200];
	char temp[200];
        float avg1, avg2, avg3;
        unsigned long total, used, mfree, shared, buffers, cached;
        unsigned int needs_conversion = 1;

        /* read memory info */
        fp = safe_fopen("meminfo", "r");

        /*
         * Old kernels (such as 2.4.x) had a nice summary of memory info that
         * we could parse, however this is gone entirely in 2.6. Try parsing
         * the old way first, and if that fails, parse each field manually.
         *
         * First, we read in the first line. Old kernels will have bogus
         * strings we don't care about, whereas new kernels will start right
         * out with MemTotal:
         *                              -- PFM.
         */
        if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
                fgets(buf, sizeof(buf), fp);    /* skip first line */

                fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
                   &total, &used, &mfree, &shared, &buffers, &cached);
        } else {
                /*
                 * Revert to manual parsing, which incidentally already has the
                 * sizes in kilobytes. This should be safe for both 2.4 and
                 * 2.6.
                 */
                needs_conversion = 0;

                fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);

                /*
                 * MemShared: is no longer present in 2.6. Report this as 0,
                 * to maintain consistent behavior with normal procps.
                 */
                if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
                        shared = 0;

                fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
                fscanf(fp, "Cached: %lu %s\n", &cached, buf);

                used = total - mfree;
        }
        fclose(fp);

	long unsigned net_recv_bytes,net_recv_packets,net_transmit_bytes,net_transmit_packets;
	fp = safe_fopen("net/dev","r");
	while(fgets(text,200,fp)){
		if(strstr(text,"eth")){
			sscanf(text,"%*[^e]"
					"%[^:]:"
					"%lu %lu "
					"%*s %*s %*s %*s %*s %*s "
					"%lu %lu ",
					buf, 
					&net_recv_bytes,&net_recv_packets, 
					&net_transmit_bytes,&net_transmit_packets);
		//	printf("recv_bytes:%lu,recv_pac:%lu,transmit_bytes:%lu,transmit_pac:%lu\n",net_recv_bytes,net_recv_packets,net_transmit_bytes,net_transmit_packets);
			memset(temp,0,200);
			sprintf(temp,"%s,%lu,%lu,%lu,%lu~",buf,net_recv_bytes,net_recv_packets,net_transmit_bytes,net_transmit_packets);
			write_buf(temp,200);

		}
	}
	fclose(fp);

        /* read load average */
        fp = safe_fopen("loadavg", "r");
        if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) {
                print_error_msg_and_die("failed to read '%s'", "loadavg");
        }
        fclose(fp);

	//printf("load average\r\n");
	/* read version */
	fp = safe_fopen("version","r");
	memset(text,0,200);
	if (fgets(text,200,fp) != NULL){
		memset(temp,0,200);
		sprintf(temp,"Version,%s~",text);
		//printf("%s",temp);
		write_buf(temp,200);
	}
	fclose(fp);

	/* read hostname*/
	fp = safe_fopen("sys/kernel/hostname","r");
	memset(text,0,200);
	if (fgets(text,200,fp) != NULL){
		memset(temp,0,200);
		sprintf(temp,"Hostname,%s~",text);
		write_buf(temp,200);
	}
	fclose(fp);

	/* read hardware info*/
	fp = safe_fopen("cpuinfo","r");
	memset(text,0,200);
	int  cpu_count = 0;
	char *p;
	while(fgets(text,200,fp)){
		p = text;
		if (strstr(text,"model name") == text){
			while(*p++!=':');
			memset(temp,0,200);
			sprintf(temp,"Cpuinfo,processor:%d,%s~",cpu_count,p);
			write_buf(temp,200);
			cpu_count++;
		}
	}
	fclose(fp);



        if (needs_conversion) {
                /* convert to kilobytes */
                used /= 1024;
                mfree /= 1024;
                shared /= 1024;
                buffers /= 1024;
                cached /= 1024;
                total /= 1024;
        }

        /* output memory info and load average */
	memset(temp,0,200);
	sprintf(temp,"Mem,%ld, %ld, %ld, %ld, %ld~", used, mfree, shared, buffers, cached);
	write_buf(temp,200);

	memset(temp,0,200);
	sprintf(temp,"LoadAverage, %.2f, %.2f, %.2f~", avg1, avg2, avg3);
	write_buf(temp,200);

        //printf("\e[H\e[J" "Mem: "
        //printf("Mem: "
        //       "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n",
        //       used, mfree, shared, buffers, cached);
       // printf("Load average: %.2f, %.2f, %.2f    "
       //                 "(State: S=sleeping R=running, W=waiting)\n",
       //        avg1, avg2, avg3);
        return total;
}


/* display process statuses */
static void display_status(int count, int col)
{
        procps_status_t *s = top;
        char rss_str_buf[8];
	char temp[200];
        unsigned long total_memory = display_generic();
	unsigned long total_cpupc = 0;

        /* what info of the processes is shown */
       // printf("\n\e[7m  PID    STATUS   RSS  PPID %%CPU %%MEM COMMAND\e[0m\n");

        while (count--) {
                char *namecmd = s->short_cmd;
                int pmem;

                pmem = 1000.0 * s->rss / total_memory;
                if (pmem > 999) pmem = 999;

                if(s->rss > 10*1024)
                        sprintf(rss_str_buf, "%6ldM", s->rss/1024);
                else
                        sprintf(rss_str_buf, "%7ld", s->rss);
               // printf("%5d %s  %s %5d %2d.%d %2u.%u ",
              //          s->pid, s->state, rss_str_buf, s->ppid,
              //          s->pcpu/10, s->pcpu%10, pmem/10, pmem%10);

		memset(temp,0,200);
                sprintf(temp,"Process,%d,%s,%s,%d,%d.%d,%u.%u,%s~",
                        s->pid, s->state, rss_str_buf, s->ppid,
                        s->pcpu/10, s->pcpu%10, pmem/10, pmem%10, namecmd);
		write_buf(temp,200);

		if(strlen(namecmd) > col)
			namecmd[col] = 0;
	//	printf("%s\n", namecmd);
		s++;

		total_cpupc += s->pcpu;
        }

	memset(temp,0,200);
	sprintf(temp,"Cpu,%lu\r\n",total_cpupc);
	write_buf(temp,200);
}

static void clearmems(void)
{
        free(top);
        top = 0;
        ntop = 0;
}


void* process_main(void* arg)
{
	process_args_t *ar =(process_args_t *)arg;
	int interval = ar->interval;
	int sockfd = ar->sockfd;
	int lines , col;
	int opt=100;

        lines = 30;
        /* Default CMD format size */
        col = 35 - 6;
        /* change to /proc */
        if (chdir("/proc") < 0) {
                print_error_msg_and_die("chdir('/proc')");
        }

        sort_function[0] = pcpu_sort;
        sort_function[1] = mem_sort;
        sort_function[2] = time_sort;
        while (1) {
                /* read process IDs & status for all the processes */
                procps_status_t * p;

                while ((p = procps_scan(0)) != 0) {
                        int n = ntop;

                        top = safe_realloc(top, (++ntop)*sizeof(procps_status_t));
                        memcpy(top + n, p, sizeof(procps_status_t));
                }
		if (ntop == 0) {
			print_error_msg_and_die("scandir('/proc')");
		}
                if(!Hertz) {
                        init_Hertz_value();
                        do_stats();
                        sleep(1);
                        clearmems();
			continue;
		}
                do_stats();
                opt = lines;
                if (opt > ntop) {
                        opt = ntop;
                }
                /* show status for each of the processes */
                display_status(opt, col);
		write_msg(sockfd,msg_buff,msg_top);
		msg_top = 0;
                clearmems();
		pthread_testcancel();
                sleep(interval);
		pthread_testcancel();
        }

       //return EXIT_SUCCESS;
}

